package com.gxar.file.server.db.service;

import com.gxar.common.entity.login.UserProfile;
import com.gxar.common.exception.APIRuntimeException;
import com.gxar.file.api.response.ProductFileBatchDeleteResponse;
import com.gxar.file.server.base.common.enums.TargetChannelEnum;
import com.gxar.file.server.base.common.enums.ProductEnum;
import com.gxar.file.server.base.common.properties.ActiveProperties;
import com.gxar.file.server.base.model.ResultCodeEnum;
import com.gxar.file.server.base.model.upload.request.FilePartUploadRequest;
import com.gxar.file.server.base.model.upload.response.FilePartUploadResponse;
import com.gxar.file.server.base.sdk.AliYunOssHelper;
import com.gxar.file.server.db.mapper.FileMapper;
import com.gxar.file.server.db.model.FileEntity;
import com.gxar.file.server.db.model.HandleEntity;
import com.gxar.file.server.db.respository.FileRepository;
import com.gxar.file.server.db.respository.HandleRepository;
import com.gxar.file.server.db.service.strategy.TargetChannelStrategyContext;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.Optional;

/**
 * 素材业务控制器
 *
 * @author Linsy
 * @version 1.0
 * @created 2023/6/20 11:44 AM
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class FileService {

    private final FileRepository fileRepository;
    private final HandleRepository handleRepository;
    private final FileMapper fileMapper;
    private final AliYunOssHelper aliYunOssHelper;
    private final TargetChannelStrategyContext channelSourceStrategyContext;
    private final ActiveProperties activeProperties;


    /**
     * 上传文件
     *
     * @param product          产品
     * @param name             文件名称
     * @param originalFilename 上传时的文件名称
     * @param file             文件
     * @param publicRead       是否公共读
     * @param userProfile      用户信息
     * @return 是否上传成功
     * @throws Exception 文件上传异常
     */
    @Transactional(rollbackFor = Throwable.class)
    public Boolean upload(ProductEnum product, String name, String originalFilename, MultipartFile file, Boolean publicRead, UserProfile userProfile) throws Exception {
        FileEntity fileEntity = new FileEntity();
        fileEntity.setName(name);
        fileEntity.setCreator(userProfile.getMobile());
        fileEntity.setCreatedAt(new Date());
        fileEntity.setProductName(product.getName());
        fileEntity.setUserId(userProfile.getId());
        fileEntity.setOriginalFilename(originalFilename);
        fileEntity.setSize(file.getSize());
        fileRepository.save(fileEntity);
        String profile = activeProperties.getProfile();
        HandleEntity handler = handleRepository.findByProductAndEnv(product.getName(), profile);

        TargetChannelEnum targetChannelEnum = TargetChannelEnum.getChannelTargetByName(handler.getHandle());
        if (Objects.isNull(targetChannelEnum)) {
            return false;
        }
        Boolean uploaded = channelSourceStrategyContext.uploadFile(targetChannelEnum, product, name, file.getInputStream(), publicRead);
        if (!uploaded) {
            throw new RuntimeException("上传失败");
        }
        return true;
    }

    /**
     * 获取上传图片的地址，只有公共读才有地址
     *
     * @param product 产品
     * @param name    上传oss的文件名
     * @return 上传图片的地址
     */
    public String getUploadUrl(ProductEnum product, String name) {
        String profile = activeProperties.getProfile();
        HandleEntity handler = handleRepository.findByProductAndEnv(product.getName(), profile);
        TargetChannelEnum targetChannelEnum = TargetChannelEnum.getChannelTargetByName(handler.getHandle());
        if (Objects.isNull(targetChannelEnum)) {
            return "该环境暂未配置目标";
        }
        return channelSourceStrategyContext.getFileUrl(targetChannelEnum, product, name);
    }

    /**
     * 下载文件
     *
     * @param product  产品
     * @param name     文件名称
     * @param response 响应体
     * @return 下载错误信息（为空表示下载成功）
     */
    public String downloadFile(ProductEnum product, String name, HttpServletResponse response) {
        String profile = activeProperties.getProfile();
        HandleEntity handler = handleRepository.findByProductAndEnv(product.getName(), profile);
        TargetChannelEnum targetChannelEnum = TargetChannelEnum.getChannelTargetByName(handler.getHandle());
        if (Objects.isNull(targetChannelEnum)) {
            return "该环境暂未配置目标";
        }
        return channelSourceStrategyContext.downloadFile(targetChannelEnum, product, name, response);
    }

    /**
     * 根据用户ID和文件名称查询文件
     *
     * @param userId      用户id
     * @param productName 产品
     * @param name        文件名称
     * @return 文件
     */
    public FileEntity findByUserIdAndName(Long userId, String productName, String name) {
        return fileRepository.findByUserIdAndName(userId, name, productName);
    }

    /**
     * 分片上传
     *
     * @param product          产品
     * @param name             文件名称
     * @param originalFilename 源文件名
     * @param file             源文件
     * @param request          分片上传请求体
     * @param userProfile      用户信息·
     * @return 分片上传响应
     */
    @Transactional(rollbackFor = Throwable.class)
    public FilePartUploadResponse uploadPart(ProductEnum product, String name, String originalFilename, MultipartFile file, FilePartUploadRequest request, UserProfile userProfile) {
        String profile = activeProperties.getProfile();
        HandleEntity handler = handleRepository.findByProductAndEnv(product.getName(), profile);
        TargetChannelEnum targetChannelEnum = TargetChannelEnum.getChannelTargetByName(handler.getHandle());
        if (Objects.isNull(targetChannelEnum)) {
            throw new APIRuntimeException(ResultCodeEnum.TEMPORARILY_UNAVAILABLE);
        }
        FilePartUploadResponse response = channelSourceStrategyContext.uploadPartFile(targetChannelEnum, product, name, request, file);
        if (Objects.isNull(response)) {
            throw new APIRuntimeException(ResultCodeEnum.UPLOAD_FAIL);
        }
        response.setKey(name);
        // 公共读才有办法通过url访问
        if (Objects.nonNull(request.getPublics()) && request.getPublics()) {
            response.setUrl(getUploadUrl(product, name));
        }
        // 文件上传完成
        if (Objects.nonNull(response.getFinish()) && response.getFinish()) {
            FileEntity fileEntity = new FileEntity();
            fileEntity.setName(name);
            fileEntity.setCreator(userProfile.getMobile());
            fileEntity.setCreatedAt(new Date());
            fileEntity.setProductName(product.getName());
            fileEntity.setUserId(userProfile.getId());
            fileEntity.setOriginalFilename(originalFilename);
            fileRepository.save(fileEntity);
        }
        return response;
    }

    /**
     * 批量文件删除
     *
     * @param userId      用户id
     * @param productName 产品名
     * @param fileUrl     文件url
     */
    public void batchFileDelete(Long userId, String productName, List<String> fileUrl) {
        Optional.ofNullable(userId).orElseThrow(() -> new APIRuntimeException(ResultCodeEnum.USER_ID_NOT_NULL));
        Optional<ProductEnum> productEnumOptional = ProductEnum.getByName(productName);
        if (!productEnumOptional.isPresent()) {
            throw new APIRuntimeException(ResultCodeEnum.TEMPORARILY_UNAVAILABLE);
        }
        if (CollectionUtils.isEmpty(fileUrl)) {
            throw new APIRuntimeException(ResultCodeEnum.URL_NOT_NULL);
        }
        List<String> fileNameList = new ArrayList<>(fileUrl.size());
        fileUrl.forEach(e -> {
            String fileName = e.substring(e.indexOf("/product") + 1);
            if (StringUtils.isNotBlank(fileName)) {
                fileNameList.add(fileName);
            }
        });
        List<FileEntity> fileEntityList = fileMapper.queryByUserIdAndName(userId, fileNameList);
        if (CollectionUtils.isEmpty(fileEntityList)) {
            return;
        }
        String profile = activeProperties.getProfile();
        HandleEntity handler = handleRepository.findByProductAndEnv(productName, profile);
        TargetChannelEnum targetChannelEnum = TargetChannelEnum.getChannelTargetByName(handler.getHandle());
        if (Objects.isNull(targetChannelEnum)) {
            throw new APIRuntimeException(ResultCodeEnum.TEMPORARILY_UNAVAILABLE);
        }
        for (FileEntity fileEntity : fileEntityList) {
            try {
                channelSourceStrategyContext.deleteFile(targetChannelEnum, productName, fileEntity.getName());
                fileRepository.delete(fileEntity);
            } catch (Exception e) {
                // 文件删除失败
            }
        }
    }

    /**
     * 产品文件批量删除
     * @param productName 产品名称
     * @param fileUrl 文件地址集合
     * @return 删除失败的文件地址
     */
    public ProductFileBatchDeleteResponse productFileBatchDelete(String productName, List<String> fileUrl) {
        log.info("开始执行产品文件删除。。。");
        Optional<ProductEnum> productEnumOptional = ProductEnum.getByName(productName);
        if (!productEnumOptional.isPresent()) {
            throw new APIRuntimeException(ResultCodeEnum.TEMPORARILY_UNAVAILABLE);
        }
        if (CollectionUtils.isEmpty(fileUrl)) {
            throw new APIRuntimeException(ResultCodeEnum.URL_NOT_NULL);
        }
        List<String> fileNameList = new ArrayList<>(fileUrl.size());
        fileUrl.forEach(e -> {
            String fileName = e.substring(e.indexOf("/product") + 1);
            if (StringUtils.isNotBlank(fileName)) {
                fileNameList.add(fileName);
            }
        });
        List<FileEntity> fileEntityList = fileMapper.queryByUserIdAndName(null, fileNameList);
        if (CollectionUtils.isEmpty(fileEntityList)) {
            return new ProductFileBatchDeleteResponse();
        }
        String profile = activeProperties.getProfile();
        HandleEntity handler = handleRepository.findByProductAndEnv(productName, profile);
        TargetChannelEnum targetChannelEnum = TargetChannelEnum.getChannelTargetByName(handler.getHandle());
        if (Objects.isNull(targetChannelEnum)) {
            throw new APIRuntimeException(ResultCodeEnum.TEMPORARILY_UNAVAILABLE);
        }
        List<String> failFile = new ArrayList<>();
        for (FileEntity fileEntity : fileEntityList) {
            try {
                channelSourceStrategyContext.deleteFile(targetChannelEnum, productName, fileEntity.getName());
                fileRepository.delete(fileEntity);
            } catch (Exception e) {
                // 文件删除失败
                String failFileUrl = channelSourceStrategyContext.getFileUrl(targetChannelEnum, productEnumOptional.get(), fileEntity.getName());
                failFile.add(failFileUrl);
            }
        }
        ProductFileBatchDeleteResponse response = new ProductFileBatchDeleteResponse();
        response.setFailFile(failFile);
        return response;
    }

}
